import datetime
import logging
import time

import httpx
import requests
from retrying import retry

from modules.storage.file_store import FileStore
from utils.os_main import get_abs_file_path

logging.basicConfig(filename=get_abs_file_path('logs/request.log'),  # Specify the file to save logs
                    level=logging.DEBUG,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


class RequestMain:
    # def __init__(self):

    # requests请求
    """
        :param method: 请求方式 get/post/put/delete/patch
        :param url: 请求地址
        :param params: 字典或bytes，作为参数增加到url中
        :param data: data类型传参，字典、字节序列或文件对象，作为Request的内容
        :param json: json传参，作为Request的内容
        :param headers: 请求头，字典
        :param files: 上传文件 例如：{'file': open('favicon.ico', 'rb')}
        :param cert: 指定本地证书做客户端证书 例如：('/path/server.crt','/path/server.key')
        :param timeout: 如果超过这个时间还没有响应，就报错 例如：timeout=1 或 timeout=(5, 30)
        :param proxies: 设置代理 例如： proxies = {'http':'http://10.10.10.10:1080','https':'http://10.10.10.10:1080',}
        :param kwargs: 若还有其他的参数，使用可变参数字典形式进行传递
        :return:
    """

    @retry(stop_max_attempt_number=5, retry_on_result=lambda re_data: re_data is None, wait_fixed=2000)
    def requests_request(self, method, url, params=None, data=None, json=None, headers=None, files=None, verify=False,
                         cert=None, timeout=None, proxies=None, proxy=None, **kwargs):

        # 对异常进行捕获
        try:
            """
                封装request请求，将请求方法、请求地址，请求参数、请求头等信息入参。
                注 ：verify: True/False，默认为True，认证SSL证书开关；cert: 本地SSL证书。如果不需要ssl认证，可将这两个入参去掉
            
                使用session管理器
                requests.session(): 维持会话,跨请求的时候保存参数   
            """

            # 处理代理
            proxies = None
            if proxy:
                proxies = {
                    'http://': 'http://' + proxy,
                    'https://': 'https://' + proxy,
                }
            re_data = requests.session().request(method, url, params=params, data=data, json=json, headers=headers,
                                                 files=files, cert=cert, timeout=timeout, verify=verify,
                                                 proxies=proxies, **kwargs)
        # 异常处理 报错显示具体信息
        except Exception as e:
            re_data = None
            # 打印异常
            print("请求失败：{0}".format(e))
            logger.error("Error occurred: %s", str(e), exc_info=True)
            self._record_failure_info(method, url, params, proxies, e)
            # 重新抛出异常，触发 retry 机制
            raise e
        # 返回响应结果
        return re_data

    def _record_failure_info(self, method, url, params, proxies, error_message):
        """
        记录请求失败信息到文件。

        Args:
            method (str): 请求方法。
            url (str): 请求地址。
            params (dict): 请求参数。
            proxies (dict): 代理设置。
            error_message (str): 错误信息。
        """
        fail_info = {
            'method': method,
            'url': url,
            'params': params,
            'proxies': proxies,
            'error': f"请求失败：{error_message}",
            'time': datetime.datetime.now(),
            'divide': f'{"=" * 50}\n',
        }
        fail_file = FileStore(get_abs_file_path('logs/failures.log'), 'a')
        fail_file.write(fail_info)


    """
        :param method: 请求方式 GET/POST/PUT/DELETE/PATCH
        :param url: 请求地址
        :param is_http2: 是否使用HTTP/2.0，要注意的是要使用HTTP/2.0 还要使用该命令安装  pip install httpx[http2]
        :param content:
        :param data:
        :param files:
        :param json:
        :param params:
        :param headers:
        :param cookies:
        :param timeout:
        :param extensions:
        :param kwargs: 若还有其他的参数，使用可变参数字典形式进行传递

        :return:
    """

    @retry(stop_max_attempt_number=5, retry_on_result=lambda re_data: re_data is None, wait_fixed=2000)
    def httpx_request(self, method, url, is_http2=False, content=None, data=None, files=None, json=None, params=None,
                      headers=None, cookies=None, timeout=None, extensions=None, proxy=None, **kwargs):

        # 对异常进行捕获
        try:
            """
                使用client  
                method.upper() 请求方法都转为大写
            """
            # 处理代理
            proxies = None
            if proxy:
                proxies = {
                    'http://': 'http://' + proxy,
                    'https://': 'https://' + proxy,
                }

            re_data = httpx.Client(http2=is_http2, proxies=proxies).request(method.upper(), url, content=content,
                                                                            data=data, files=files, json=json,
                                                                            params=params, headers=headers,
                                                                            cookies=cookies, timeout=timeout,
                                                                            extensions=extensions, **kwargs)
        # 异常处理 报错显示具体信息
        except Exception as e:
            re_data = None
            # 打印异常
            print("请求失败：{0}".format(e))
            logger.error("Error occurred: %s", str(e), exc_info=True)
            self._record_failure_info(method, url, params, proxies, e)
            # 重新抛出异常，触发 retry 机制
            raise e
        # 返回响应结果
        return re_data

    @retry(stop_max_attempt_number=5, retry_on_result=lambda re_data: re_data is None, wait_fixed=2000)
    def request(self, method, url, is_http2=False, params=None, data=None, json=None, headers=None, files=None,
                verify=False, cert=None, timeout=None, proxies=None, content=None, cookies=None, extensions=None,
                **kwargs):

        try:
            if is_http2:
                re_data = self.httpx_request(method=method.upper(), url=url, is_http2=is_http2, content=content,
                                             data=data, files=files, json=json, params=params, headers=headers,
                                             cookies=cookies, timeout=timeout, extensions=extensions, **kwargs)
            else:
                re_data = self.requests_request(method=method, url=url, params=params, data=data, json=json,
                                                headers=headers, files=files, cert=cert, timeout=timeout, verify=verify,
                                                proxies=proxies, **kwargs)

        # 异常处理 报错显示具体信息
        except Exception as e:
            re_data = None
            # 打印异常
            print("请求失败：{0}".format(e))
            logger.error("Error occurred: %s", str(e), exc_info=True)
            self._record_failure_info(method, url, params, proxies, e)
            # 重新抛出异常，触发 retry 机制
            raise e
        # 返回响应结果
        return re_data

    @retry(stop_max_attempt_number=5, retry_on_result=lambda re_data: re_data is None, wait_fixed=2000)
    def request_with_time_sleep(self, timesecond, method, url, is_http2=False, params=None, data=None, json=None, headers=None, files=None,
                verify=False, cert=None, timeout=None, proxies=None, content=None, cookies=None, extensions=None,
                **kwargs):

        # 请求前暂停一段时间 单位是s
        time.sleep(timesecond)

        self.request(self, method, url, is_http2=False, params=None, data=None, json=None, headers=None, files=None,
                    verify=False, cert=None, timeout=None, proxies=None, content=None, cookies=None, extensions=None,
                    **kwargs)



# 生成Request实例
request_main = RequestMain()

if __name__ == '__main__':

    # request_requests 使用requests请求
    request_data = request_main.requests_request("get", 'https://spa16.scrape.center/')
    if request_data:
        print(request_data.text)
        print(request_data.code)

    # httpx 请求HTTP/2.0
    # response = re.httpx_request('GET', 'https://spa16.scrape.center/', True)
    # httpx 一般请求
    # headers = {'User-Agent': 'my-app/0.0.1'}
    # response = re.httpx_request('get', 'https://www.httpbin.org/get',params={'name': 'germey'})
    # print(response.text)
    # print(response.code)

    print(datetime.datetime.now())
